package com.spring.core;

import com.spring.core.annotation.*;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * 简易Spring容器，支持单例Bean的三级缓存与循环依赖
 */
public class ApplicationContext implements BeanFactory {
    // 一级缓存：完全初始化好的单例Bean
    private final Map<String, Object> singletonObjects = new HashMap<>();
    // 二级缓存：实例化但未依赖注入的早期Bean
    private final Map<String, Object> earlySingletonObjects = new HashMap<>();
    // 三级缓存：Bean工厂
    private final Map<String, ObjectFactory> singletonFactories = new HashMap<>();
    // Bean定义：beanName -> BeanClass
    private final Map<String, Class<?>> beanDefinitionMap = new HashMap<>();
    // Bean作用域：beanName -> scope
    private final Map<String, String> beanScopeMap = new HashMap<>();

    private interface ObjectFactory {
        Object getObject() throws Exception;
    }

    public ApplicationContext(String basePackage) {
        try {
            // 扫描所有定义域内的文件，找Spring的组件，将其相应内容放入beanDefinitionMap和beanScopeMap
            scanPackage(basePackage);
            // 初始化所有单例Bean
            for (String beanName : beanDefinitionMap.keySet()) {
                if ("singleton".equals(beanScopeMap.get(beanName))) {
                    getBean(beanName);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 扫描包，注册Bean定义和作用域
    private void scanPackage(String basePackage) throws Exception {
        // 拿到所有的字节码文件
        String path = basePackage.replace('.', '/');
        URL resource = Thread.currentThread().getContextClassLoader().getResource(path);
        if (resource == null) return;
        File dir = new File(resource.toURI());
        if (!dir.exists() || !dir.isDirectory()) return;
        File[] files = dir.listFiles();
        if (files == null) return;
        for (File file : files) {
            // 如果这个文件是个目录，那么递归进去处理
            if (file.isDirectory()) {
                scanPackage(basePackage + "." + file.getName());
            }
            // 如果文件是字节码文件，则进入，看看它是不是Bean
            else if (file.getName().endsWith(".class")) {
                String className = basePackage + '.' + file.getName().replace(".class", "");
                Class<?> clazz = Class.forName(className);
                // 如果这个class是Spring的Bean组件
                if (isSpringComponent(clazz)) {
                    String beanName = getBeanName(clazz);
                    String scope = "singleton";
                    if (clazz.isAnnotationPresent(Scope.class)) {
                        scope = clazz.getAnnotation(Scope.class).value();
                    }
                    beanDefinitionMap.put(beanName, clazz);
                    beanScopeMap.put(beanName, scope);
                }
            }
        }
    }

    // 判断是否为Spring组件
    private boolean isSpringComponent(Class<?> clazz) {
        return clazz.isAnnotationPresent(Component.class)
                || clazz.isAnnotationPresent(Service.class)
                || clazz.isAnnotationPresent(Repository.class)
                || clazz.isAnnotationPresent(Controller.class);
    }

    // 获取Bean名称
    private String getBeanName(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Component.class)) {
            String v = clazz.getAnnotation(Component.class).value();
            if (!v.isEmpty()) return v;
        }
        if (clazz.isAnnotationPresent(Service.class)) {
            String v = clazz.getAnnotation(Service.class).value();
            if (!v.isEmpty()) return v;
        }
        if (clazz.isAnnotationPresent(Repository.class)) {
            String v = clazz.getAnnotation(Repository.class).value();
            if (!v.isEmpty()) return v;
        }
        if (clazz.isAnnotationPresent(Controller.class)) {
            String v = clazz.getAnnotation(Controller.class).value();
            if (!v.isEmpty()) return v;
        }
        String name = clazz.getSimpleName();
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }

    // Bean创建主流程
    private Object doCreateBean(String beanName, Class<?> clazz) {
        // 1. 先查一级缓存
        Object bean = singletonObjects.get(beanName);
        if (bean != null) return bean;
        // 2. 查二级缓存
        bean = earlySingletonObjects.get(beanName);
        if (bean != null) return bean;
        // 3. 查三级缓存
        ObjectFactory factory = singletonFactories.get(beanName);
        if (factory != null) {
            try {
                return factory.getObject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        // 4. 通过反射创建Bean实例，放入三级缓存
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            singletonFactories.put(beanName, () -> instance);
            earlySingletonObjects.put(beanName, instance);
            // 依赖注入
            injectDependencies(instance, clazz);
            // 完成后放入一级缓存
            singletonObjects.put(beanName, instance);
            earlySingletonObjects.remove(beanName);
            singletonFactories.remove(beanName);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 依赖注入
    private void injectDependencies(Object bean, Class<?> clazz) throws IllegalAccessException {
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                Object dependency;
                if (field.isAnnotationPresent(Qualifier.class)) {
                    String depName = field.getAnnotation(Qualifier.class).value();
                    dependency = getBean(depName);
                } else {
                    dependency = getBean(field.getType());
                }
                field.set(bean, dependency);
            }
        }
    }

    @Override
    public Object getBean(String name) {
        String scope = beanScopeMap.get(name);
        Class<?> clazz = beanDefinitionMap.get(name);
        if (clazz == null) throw new RuntimeException("No bean named " + name + " found");
        if ("singleton".equals(scope)) {
            Object bean = singletonObjects.get(name);
            if (bean != null) return bean;
            return doCreateBean(name, clazz);
        } else if ("prototype".equals(scope)) {
            try {
                Object instance = clazz.getDeclaredConstructor().newInstance();
                injectDependencies(instance, clazz);
                return instance;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        throw new RuntimeException("Unknown scope: " + scope);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) {
        // 先查一级缓存：如果缓存中有，直接返回对象
        for (Map.Entry<String, Object> entry : singletonObjects.entrySet()) {
            if (requiredType.isInstance(entry.getValue())) {
                return requiredType.cast(entry.getValue());
            }
        }
        // 再查定义
        for (Map.Entry<String, Class<?>> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            Class<?> clazz = entry.getValue();
            if (requiredType.isAssignableFrom(clazz)) {
                return requiredType.cast(getBean(beanName));
            }
        }
        throw new RuntimeException("No bean of type " + requiredType + " found");
    }
} 